package model;

import java.awt.image.BufferedImage;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;

import controller.ConcreteFactory;

/**
 * La classe ConcreteBmpImage hérite de la classe abstraite Image. Elle contient 
 * les méthodes pour fournir les information d'une image BMP. ConcreteBmpImage
 * va envoyer ses informations à ProxyImage.
 * 
 * @date 25-09-2014
 * @author Fredy A. Bonilla et Emile Robinson
 *
 */
public class ConcreteBmpImage extends Image{

	//Nombre magique correspondant à l'utilisation du fichier BMP bftype
	private final static int BMP_CODE = 0x424d;
	
	//C'est la constante utilisé pour effectuer la comparaison des bits
	private final static int MASK = 0xFF; 
	
	//Variables
	private BufferedImage imageBMP=null;
	
	/**
	 * Contructeur de la classe ConcreteBmpImage. Elle reçoit en paramètre le 
	 * fichier image generée par ConcreteFactory.
	 * @param fichier 
	 */
	public ConcreteBmpImage(File file){
		super._file=file;
		init();
	}


	/****************************************************
	 * Méthodes
	 ****************************************************/
	
	/**
	 * La methode init va appeller la fonction privee LireBMP qui retourne une
	 * image de type bmp et l'assigne à l'attribut local imageBMP. 
	 */
	
	private void init() {
		imageBMP = lireBMP(super._file);
	}
	
	/**
	 * Stratégie: Consulter seulement le header du fichier afin de creer une 
	 * image de type BufferedImage. Pour recuperer les informations necessaires
	 * il faut se deplacer dans la memoire (offset)
	 * 
	 * @param nomFichier
	 * @return une image generé du fichier
	 */
	private BufferedImage lireBMP(File fichier){
		
		//Variables
		int largeur=0;
		int hauteur=0;
		
		
		try{
			DataInputStream inputBMP = new DataInputStream
					(new FileInputStream(fichier));
			
			//le debut de l'entete a un offset de 18
			inputBMP.skipBytes(18);
			
			//La methode lireEntier nous retourne les valeurs int des largeur
			//et hauteur
			largeur = lireEntier(inputBMP);
			hauteur = lireEntier(inputBMP);
			
			//On creer une nouvelle image. 
			BufferedImage image = new BufferedImage(largeur, hauteur, 
					BufferedImage.TYPE_INT_RGB);
			
			//Les pixels de couleur se trouvent au offset 28
			inputBMP.skipBytes(28);

			/*
			 * Dans la boucle ci-dessous, on parcourt l'image sous forme de 
			 * matrice (hauteur*largeur). Étant donné que l'on commence par le 
			 * haut, la premiere boucle (hauteur) va en décrémentation tandis 
			 * que la 2e boucle an incrémentation (largeur). 
			 */
			for(int j = hauteur - 1; j >= 0; j--){
				for(int i = 0; i < largeur; i++){
					
					/*
					 * Appel de la méthode lireCouleur pour recupérer les 
					 * couleurs et pour la configurer dans l'image créée. 
					 */
					image.setRGB(i, j, lireCouleur(inputBMP));
				}
			}
			
			//Fermeture du flux de donnée
			inputBMP.close();
			
			return image;
			
		}
		catch(Exception e){
			return null;
		}
	
	}
	
	/**
	 * Cette méthode a pour but de comparer les bit avec la constante MASK afin 
	 * d'obtenir une somme qui constitura la couleur. 
	 * @param input
	 * @return la couleur RGB du pixel. 
	 */
	private static int lireCouleur(DataInputStream input){
		 
		byte[] tab = new byte[3]; //tableau RGB
		int couleur = 0;

		try{
			input.read(tab);  
			
			//comparaison, deplacements des bits et addition
			couleur = tab[0] & MASK;
			couleur = couleur + ((tab[1] & MASK) << 8);
			couleur = couleur + ((tab[2] & MASK) << 16);
		}
		catch(Exception e){

		}

		//retourne la couleur RGB
		return couleur;
	}
	
	/**
	 * La methode LireEntier permet de lire un entier en executant des 
	 * operations binaires sur les données. 
	 * @param input
	 * @return entier, la valeur de type int correspondante. 
	 */
	private static int lireEntier(DataInputStream input){
		
		//Variables
		byte[] tab = new byte[4];
		int entier = 0;

		try{
			input.read(tab);
			
			//comparaison, deplacements des bits et addition
			entier = tab[0] & MASK;
			entier = entier + ((tab[1] & MASK) << 8);
			entier = entier + ((tab[2] & MASK) << 16);
			entier = entier + ((tab[3] & MASK) << 24);

		}
		catch(Exception e){

		}
		return entier;
	}
	/**
	 * Create a BufferedImage object to match the display's RGB requirements. 
	 * @return A BufferedImage representing the image file, or null if
	 * something went wrong.
	 */
	public BufferedImage draw() {
		
		if(imageBMP != null)
			return imageBMP;
		return null;
	}
	
	/**
	 * 
	 * @return The Image's height
	 */
	public int getHeight() {
		return imageBMP.getHeight();
	}
	
	/**
	 * 
	 * @return The Image's width
	 */
	public int getWidth() {
		return imageBMP.getWidth();
	}
}
